/**
 * The MIT License	
 * 
 * Copyright (c) 2009 Ghost Interactive
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * ghostinteractive@gmail.com
 * http://ghostinteractive.net/blog
 * @author Jeremy brown
 */

package net.ghostinteractive.overlays
{
	import flash.display.*;
	import flash.geom.*;
	
	import org.openzoom.flash.components.MultiScaleImage;
	import org.openzoom.flash.descriptors.IMultiScaleImageDescriptor;
	import org.openzoom.flash.events.ViewportEvent;
	
	public class PolylineLayer extends Sprite
	{
		protected var _segments	:Array;
		protected var _image		:MultiScaleImage;
		
		/**
		 * Constructor
		 * A PolylineLayer draws a layer of relatively scaled paths using flash.display.Graphics.lineTo()
		 * over a MultiScaleImage
		 * 
		 * @param MultiScaleImage to sync this layer with
		 */
		public function PolylineLayer( image:MultiScaleImage )
		{
			super();
			mouseEnabled 	= false;
			mouseChildren 	= false;
			
			_segments = [];
			cacheAsBitmap = true;
			if( image ) { multiScaleImage = image; }
		}
		
		// -----------------------------------------------------
		//
		//	Public
		//	
		// -----------------------------------------------------
		
		/**
		 * Setting this param essentially syncs polyline layer to a MultiScaleImage instance 
		 * by aligning and setting it's size to the same dimensions as the image's viewport,
		 * adding event listeners to the image's viewport
		 * Calls purge() method to remove listeners to previous MultiScaleImage and clear graphics
		 * 
		 * @param _image MultiScaleImage instance for polyline layer to sync with
		 */
		public function set multiScaleImage( image:MultiScaleImage ):void
		{
			if( _image ) { purge() };
			
			_image 	= image;
			x 		= _image.x;
			y 		= _image.y;
	
			setActualSize( _image.viewport.viewportWidth, _image.viewport.viewportHeight );
			
			addListeners();
		}
		
		/**
		 * Adds a polyline object to the layer
		 * 
		 * @param polyline An instance or sub-class of the AbstractPolyline class
		 */
		public function addSegment( segment:PolylineSegment ):void
		{
			_segments.push( segment );
		}
		
		/**
		 * Removes a polyline from the layer, this will not destroy the graphic until
		 * onTransformUpdate is called
		 * 
		 * @param polyline An instance or sub-call of the AbstractPolyline class
		 */
		public function removeSegment( segment:PolylineSegment ):void
		{
			var index:int = _segments.indexOf( segment );
			if( index >= 0 ) {
				_segments.splice( index, 1 );
			}
		}
		
		/**
		 * Applies a scroll rect to the instance of the PolylineLayer
		 * 
		 * @param w Width of the scroll rect to apply to this layer
		 * @param h Height of the scroll rect to apply to this layer
		 */
		public function setActualSize( w:Number, h:Number ):void
		{
			scrollRect = new Rectangle( 0, 0, w, h );
		}
		
		/**
		 * Invokes the transform update method which draws polylines
		 */
		public function validateNow():void { onTransformUpdate(); }
		
		
		public function clear():void { graphics.clear(); }
		
		/**
		 * Clears all graphics, removes listeners, and empties array of poly line objects
		 * Once called a new MultiScaleImage must be assigned, to again activate this layer
		 */
		public function purge():void
		{
			graphics.clear();
			removeListeners();
			_segments.splice( 0 );
		}
		
		override public function toString():String { return 'PolylineLayer'; }
		
		// -----------------------------------------------------
		//
		//	PROTECTED
		//	
		// -----------------------------------------------------
		
		// -----------------------------------------------
		//	Event Handlers
		// -----------------------------------------------
		
		/**
		 * Loops through the list of polylines and draws connecting lines
		 * relative to the scale of the zoom image
		 */
		protected function onTransformUpdate( event:ViewportEvent = null ):void
		{
			var segment:PolylineSegment;

			if( !_image.source ) return; 
				
			var w:Number = IMultiScaleImageDescriptor( _image.source ).width;
			var h:Number = IMultiScaleImageDescriptor( _image.source ).height;
			var xPercStart	:Number;			
			var yPercStart	:Number;
			var xPercEnd	:Number;
			var yPercEnd	:Number;
			var startLoc	:Point = new Point();
			var endLoc		:Point = new Point();
			var startTarg	:Point = new Point();
			var endTarg		:Point = new Point();
			var prevEnd		:Point = new Point();
			var g			:Graphics = graphics;
				
			g.clear();
				
			for each( segment in _segments )
			{
				xPercStart = segment.start.x / w;
				yPercStart = segment.start.y / h;
				
				xPercEnd   = segment.end.x / w;
				yPercEnd   = segment.end.y / h;
				
				startLoc.x 	= _image.sceneWidth * xPercStart;
				startLoc.y 	= _image.sceneHeight * yPercStart;
				
				endLoc.x	= _image.sceneWidth * xPercEnd;
				endLoc.y 	= _image.sceneHeight * yPercEnd;
				
				startTarg 	= _image.sceneToLocal( startLoc );
				endTarg		= _image.sceneToLocal( endLoc );
			
				g.lineStyle( segment.thickness, 
							 segment.color, 
							 segment.alpha, 
							 true, 
							 LineScaleMode.NONE, 
							 segment.capsStyle,
							 segment.jointStyle,
							 segment.miterLimit );
							 
				
				if( prevEnd.x == startTarg.x && prevEnd.y == startTarg.y ) {
					g.lineTo( endTarg.x, endTarg.y );
				} else {
					g.moveTo( startTarg.x, startTarg.y );
					g.lineTo( endTarg.x, endTarg.y );
				}
			
				prevEnd = endTarg;
			}
		}
	
		/**
		 *	Removes listeners, called from purge method
		 */
		protected function removeListeners():void
		{
			_image.viewport.removeEventListener( ViewportEvent.TRANSFORM_UPDATE, onTransformUpdate );
		}
		
		/**
		 * Adds listeners, called in image setter
		 */
		protected function addListeners():void
		{
			_image.viewport.addEventListener( ViewportEvent.TRANSFORM_UPDATE, onTransformUpdate );
		}
		
	}
}